package ai.phase.cognition.development;
import ai.*;
import ai.filetools.*;
import java.util.*;

public class Map implements LineInputProcessor {

	private int j_Height;
	private int j_Width;
	private int j_LifeSteps;
	private IntPair[] j_AgentStartPositions;
	private int j_LifeStepMilli;
	private LinkedList<MapAgent> j_AgentList;
	private Hashtable<MapAgent,IntPair> j_AgentPositions;
	private LinkedList<Frame> j_FrameList;
	private Frame[] j_FrameArray;
	private String j_IndexFileFullName;
	private Hashtable<String,ParameterParser> j_ParameterSet;
	private String j_InputFolderFullPath;
	private String j_FrameFileIndicator="framefiles";
	boolean j_ProcessingParameters;
	Frame j_BaseFrame;
	private int j_AgentIndex;
	private RegionDesignator[] j_RegionArray;
	private int j_MinimumUtilityForPosition=-9;
	private String j_TraceFileFullName;
	
	public Map(String indexFileFullName)
	{
		j_TraceFileFullName="E:\\AI_system\\Phase1.5\\map_trace.log";
		j_IndexFileFullName=indexFileFullName;
		j_ParameterSet = new Hashtable<String,ParameterParser>();
		j_ProcessingParameters=true;
		j_FrameList= new LinkedList<Frame>();
		j_AgentIndex=0;
		j_AgentList = new LinkedList<MapAgent>();
		j_AgentPositions = new Hashtable<MapAgent, IntPair>();
		SetParameterParsers();
		ProcessMapIndex(j_IndexFileFullName);
	}
	
	
	private void SetParameterParsers()
	{
		j_ParameterSet.put("startpositions", GetAgentPositionListParser());
		j_ParameterSet.put("width", GetWidthParser());
		j_ParameterSet.put("height", GetHeightParser());
		j_ParameterSet.put("loopintervalmilli", GetLifeIntervalParser());
		j_ParameterSet.put("maxiterations", GetMaxIterParser());
		j_ParameterSet.put("framefolderfullpath", GetInputFolderParser());
	}
	
	
	private ParameterParser GetAgentPositionListParser()
	{
		return new ParameterParser()
		{
			public void ProcessValues(String pointList)
			{
				j_AgentStartPositions=AITools.ParsePointList(pointList);
			}
		};
	}
	
	
	private ParameterParser GetWidthParser()
	{
		return new ParameterParser()
		{
			public void ProcessValues(String widthString)
			{
				j_Width=GetNumericParameter(widthString);
			}
		};
	}
	
	private ParameterParser GetHeightParser()
	{
		return new ParameterParser()
		{
			public void ProcessValues(String heightString)
			{
				j_Height=GetNumericParameter(heightString);
			}
		};
	}
	
	private ParameterParser GetMaxIterParser()
	{
		return new ParameterParser()
		{
			public void ProcessValues(String numberSteps)
			{
				j_LifeSteps=GetNumericParameter(numberSteps);
			}
		};
	}
	
	private ParameterParser GetLifeIntervalParser()
	{
		return new ParameterParser()
		{
			public void ProcessValues(String lifeIntervalMilli)
			{
				j_LifeStepMilli=GetNumericParameter(lifeIntervalMilli);
			}
		};
	}
	
	private ParameterParser GetInputFolderParser()
	{
		return new ParameterParser()
		{
			public void ProcessValues(String inputFolderString)
			{
				j_InputFolderFullPath=inputFolderString.trim();
			}
		};
	}
	
	
	
	private int GetNumericParameter(String lineInput)
	{
		return Integer.parseInt(lineInput.replace(" ", ""));
	}
	
	public LinkedList<MapAgent> GetAgents()
	{
		return j_AgentList;
	}
	
	public void ProcessMapIndex(String indexFileFullName) 
	{
		// Parse index for global frame characteristics
		StandardTools.ProcessFile(indexFileFullName, this);
		j_RegionArray = new RegionDesignator[j_Width*j_Height];
		for (int i=0;i<j_Width;i++)
			for (int j=0;j<j_Height;j++)
				j_RegionArray[AITools.PointToIndex(j_Width, j_Height, i, j)]=new RegionDesignator(AITools.PointToIndex(j_Width, j_Height, i, j),0);
		j_FrameArray = j_FrameList.toArray(new Frame[0]);
	}
	
	public void ProcessLine(String lineInput)
	{
		
		if (lineInput.length()>0)
		{
			if (j_ProcessingParameters)
			{
				if (j_FrameFileIndicator.equals(AITools.SimplifyString(lineInput)))
				{
					j_ProcessingParameters=false;
				}
				else
				{
					String[] keyValueParts=lineInput.split("=");
					String key = AITools.SimplifyString(keyValueParts[0]);
					String value = AITools.SimplifyString(keyValueParts[1]);
					ParameterParser parser = j_ParameterSet.get(key);
					parser.ProcessValues(value);
				}
			}
			else
			{
				String frameFileFullName=j_InputFolderFullPath+lineInput.trim();
				Frame nextFrame = new Frame(j_Width,j_Height,frameFileFullName);
				if (j_FrameList.isEmpty())
					j_BaseFrame=nextFrame;
				j_FrameList.add(nextFrame);
				
			}
		}
	}
	
	public void AddAgent(MapAgent agent)
	{
		j_AgentList.add(agent);
		if (j_AgentIndex>(j_AgentStartPositions.length-1))
			j_AgentPositions.put(agent, AITools.GetRandomIntPair(0,0,j_Width-1,j_Height-1));
		else
		{
			j_AgentPositions.put(agent, j_AgentStartPositions[j_AgentIndex]);
			j_AgentIndex++;
		}
	}
	
	private void UpdateRegions(Frame updateFrame)
	{
		RegionDeltaSpec delta;
		for (int i=0;i<j_RegionArray.length;i++)
		{
			delta=updateFrame.GetDeltaSpec(i);
			delta.UpdateRegion(j_RegionArray[i]); 
		}
		
	}
	
	public void SimpleProcessMap()
	{
		int totalFrames=j_FrameArray.length;
		
		for (int i=0;i<j_LifeSteps;i++)
		{
			UpdateRegions(j_FrameArray[i%totalFrames]);
			ProcessAllAgents();
		}
	}
	
	private void ProcessAllAgents()
	{
		IntPair agentPosition;

		Hashtable<Integer, Integer> actionRegionMap= new Hashtable<Integer,Integer>();
		Hashtable<Integer, Integer> regionActionMap = new Hashtable<Integer,Integer>();
		IntPair reachablePos;
		int[] reachableRegions;
		int[] actionList;
		Integer actionChoiceIndicator;
		int agentIndex=0;
		String traceMessage;
		for (MapAgent agent:j_AgentList)
		{
			
			agentPosition=j_AgentPositions.get(agent);
			reachableRegions=GetReachableRegions(agentPosition);
			actionList = new int[reachableRegions.length];
			//traceMessage=String.format("Agent %1$s position is: (%2$s,%3$s)",agentIndex++,agentPosition.x, agentPosition.y);
			traceMessage=String.format("(%1$s,%2$s)",agentPosition.x, agentPosition.y);
			for (int i=0;i<actionList.length;i++)
			{
				
				reachablePos=AITools.IndexToPair(j_Width,j_Height,reachableRegions[i]);
				actionList[i]=GetDisplacementIndicator(agentPosition,reachablePos);
				actionRegionMap.put(new Integer(actionList[i]), new Integer(reachableRegions[i]));
				regionActionMap.put(new Integer(reachableRegions[i]), new Integer(actionList[i]));
			}
			StandardTools.WriteTraceLog(j_TraceFileFullName, traceMessage);
			actionChoiceIndicator=agent.UpdateCurrentState(
					j_RegionArray[AITools.PointToIndex(j_Width, j_Height, agentPosition)].GetId() ,
					actionList, 
					j_RegionArray[AITools.PointToIndex(j_Width, j_Height, agentPosition)].GetUtility(),
					j_LifeStepMilli);
//			if (actionChoiceIndicator!=null)
//				StandardTools.WriteTraceLog(j_TraceFileFullName, String.format("Action choice was: %1$s",actionChoiceIndicator));
//			else
//				StandardTools.WriteTraceLog(j_TraceFileFullName, "No action taken.");
			if (actionChoiceIndicator!=null)
			{
				// Get region index of the current choice
				// map index back into a IntPair
				j_AgentPositions.put(agent, AITools.IndexToPair(j_Width,j_Height,actionRegionMap.get(actionChoiceIndicator)));
			}
		}
	}
	// 
	private int[] GetReachableRegions(IntPair position)
	{
		int[] surroundingRegionIndices=AITools.GetSurroundingIndices(position.x, position.y, 0, 0, j_Width-1, j_Height-1, j_Width, j_Height);
		
		// You can thank the Java gods for the nastiness below
		int count=0;
		
		for (int index:surroundingRegionIndices)
		{
			if (j_RegionArray[index].GetUtility()>j_MinimumUtilityForPosition)
				count++;
		}
		int[] out = new int[count];
		count=0;
		for (int index:surroundingRegionIndices)
		{
			if (j_RegionArray[index].GetUtility()>j_MinimumUtilityForPosition)
				out[count++]=index;
		}
		return out;
	}
	
	private int GetDisplacementIndicator(IntPair startPos, IntPair endPos)
	{
		return (startPos.x - endPos.x+1)+ 3*(startPos.y - endPos.y+1);
	}
}
